using System;
using System.Diagnostics;

using org.fourzoas.noise;
using org.fourzoas.noise.model;
using org.fourzoas.noise.module;

namespace org.fourzoas.noise.model {
  /// <summary>
  /// Model that defines the surface of a sphere
  /// </summary>
  /// <remarks>
  /// <img src="file://C:/temp/noise/noise/doc/html/modelsphere.png"/>
  /// <para>
  /// This model returns an output value from a noise module given the coordinates of an input value located
  /// on the suface of a sphere.
  /// </para>
  /// <para>
  /// To generate an output value, pass the (latitude, longitude) coordinates of an input value to the
  /// <see cref="GetValue(double[])"/> or <see cref="GetValue(DMS,DMS)"/> methods.
  /// </para>
  /// <para>
  /// This model is useful for creating:
  /// <list type="bullet">
  /// <item><description>seamless textures that can be mapped onto a sphere</description></item>
  /// <item><description>terrain height maps for entire planets</description></item>
  /// </list>
  /// </para>
  /// <para>
  /// This sphere has a radius of 1.0 units and its center is located at the origin.
  /// </para>
  /// </remarks>
  public class Sphere : Model {
    /// <summary>
    /// Default constructor
    /// </summary>
    public Sphere() : base() { }

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="module">The noise module that is used to generate the output values.</param>
    public Sphere(Module module) : base(module) { }

    /// <summary>
    /// Returns the output value from the noise module given the (latitude, longitude) coordinates of the 
    /// specified input value located on the surface of the sphere.
    /// </summary>
    /// <param name="coords">The latitude and longitude of the input value, in degrees.</param>
    /// <returns>The output value from the noise module.</returns>
    /// <remarks>
    /// <para>
    /// This output value is generated by the noise module passed to the <see cref="Model.Module"/> property.
    /// </para>
    /// <para>
    /// Use a negative latitude if the input value is located on the southern hemisphere.
    /// </para>
    /// <para>
    /// Use a negative longitude if the input value is located on the western hemisphere.
    /// </para>
    /// <h5>Preconditions</h5>
    /// <para>
    /// A noise module was passed to the constructor or the <see cref="Model.Module"/> property.
    /// </para>
    /// </remarks>
    public override double GetValue(params double[] coords) {
      Debug.Assert(m_Module != null);
      Debug.Assert(coords.Length == 2);

      double x, y, z;
      Globals.LatLonToXYZ(coords[0], coords[1], out x, out y, out z);
      return m_Module.GetValue(x, y, z);
    }

    /// <summary>
    /// Returns the output value from the noise module given the (latitude, longitude) coordinates of the 
    /// specified input value located on the surface of the sphere.
    /// </summary>
    /// <param name="latitude">The latitude of the input value, in <see cref="DMS"/> format.</param>
    /// <param name="longitude">The longitude of the input value, in <see cref="DMS"/> format.</param>
    /// <returns>The output value from the noise module.</returns>
    /// <remarks>
    /// <para>
    /// This output value is generated by the noise module passed to the <see cref="Model.Module"/> property.
    /// </para>
    /// <para>
    /// Use a negative latitude if the input value is located on the southern hemisphere.
    /// </para>
    /// <para>
    /// Use a negative longitude if the input value is located on the western hemisphere.
    /// </para>
    /// <h5>Preconditions</h5>
    /// <para>
    /// A noise module was passed to the constructor or the <see cref="Model.Module"/> property.
    /// </para>
    /// </remarks>
    public double GetValue(DMS latitude, DMS longitude) {
      return GetValue(latitude.ToDegrees(), longitude.ToDegrees());
    }
  }
}
